using System;
using System.Collections;
using System.Xml.Serialization;

namespace WebApplication
{
	[Serializable]
	public class PlayerCollection : ICollection
	{
		private ArrayList  _aPlayers;

		public Player this [int index]   // indexer declaration
		{ 
			get
			{
				return (Player) _aPlayers[index];
			}

			set
			{
				_aPlayers[index] = value;
			}
		}

		public Player this [string sName]   // indexer declaration
		{ 
			get
			{
				foreach (Player player in _aPlayers)
				{
					if (player.Name == sName)
						return player;
				}

				return (Player) _aPlayers[0];
			}

			set
			{
				int i = 0;

				foreach (Player player in _aPlayers)
				{
					if (player.Name == sName)
					{
						_aPlayers[i] = value;
						break;
					}

					i++;
				}
			}
		}


		public void CopyTo(Array a, int index)
		{
			_aPlayers.CopyTo(a, index);
		}
		public object SyncRoot
		{
			get{return this;}
		}
		public bool IsSynchronized
		{
			get{return false;}
		}

		public int Count
		{
			get
			{
				return _aPlayers.Count;
			}
		}

		public PlayerCollection ()
		{
			_aPlayers = new ArrayList ();
		}

		public void Add (System.Object player)
		{
			_aPlayers.Add (player);
		}

		public void Add (Player player)
		{
			_aPlayers.Add (player);
		}

		public void Remove (Player player)
		{
			_aPlayers.Remove (player);
		}

		// IEnumerable Interface Implementation:

		public IEnumerator GetEnumerator() // non-IEnumerable version
		{
			return _aPlayers.GetEnumerator ();
		}

		IEnumerator IEnumerable.GetEnumerator() // IEnumerable version
		{
			//			return (IEnumerator) new PlayerEnumerator(this);
			return _aPlayers.GetEnumerator ();
		}
	}

	[Serializable]
	public class Player
	{
		public	int[]	_nNumShares		= new int[Game.NumberOfCompanies];
		public	bool[]	_bPresShares	= new bool[Game.NumberOfCompanies];
		public	bool[]	_bSoldShares	= new bool[Game.NumberOfCompanies];
				
		private	Game	_game;	// current game object
        private bool    _soldSomething;
        private bool    _purchasedSomething;
        private int     _companiesOwned;
        private int     _companiesActedOn;
        
		//[XmlIgnore]  // TODO  Jeff, set this backpointer up when loading.
		public Game Game
		{
			get { return _game; }
			set { _game = value; }
		}

		public bool SoldSomething
		{
            get { return _soldSomething; }
            set { _soldSomething = value; }
		}

        public bool PurchasedSomething
        {
            get { return _purchasedSomething; }
            set { _purchasedSomething = value; }
        }

        public int CompaniesOwned
        {
            get { return _companiesOwned; }
            set { _companiesOwned = value; }
        }

        public int CompaniesActedOn
        {
            get { return _companiesActedOn; }
            set { _companiesActedOn = value; }
        }

        public Player()
		{
			cash = 0;
			availableCash = 0;
			_aPrivateCompanies = new PrivateCompanies ();
			_shares = new ShareCollection ();
			priority = false;
            _soldSomething = false;
            _purchasedSomething = false;
            _companiesOwned = 0;
            _companiesActedOn = 0;
        }

		public Player (string sName)
		{
			name = sName;
			cash = 0;
			availableCash = 0;
			_aPrivateCompanies = new PrivateCompanies ();
			_shares = new ShareCollection ();
			priority = false;
		}
		
		private bool priority;
		public bool Priority
		{
			get
			{
				return priority;
			}
			set
			{
				priority = value;
			}
		}

		
		private string name;
		public string Name
		{
			get
			{
				return name;
			}

			set
			{
				name = value;
			}
		}
		
        public bool UpdateScreen { get; set; }
		
		private int cash;
		public int Cash
		{
			get
			{
				return cash;
			}

			set 
			{
				cash = value;
			}
		}


		private int availableCash;
		public int AvailableCash
		{
			get
			{
				return availableCash;
			}

			set 
			{
				availableCash = value;
			}
		}

		
		private PrivateCompanies _aPrivateCompanies;
		public PrivateCompanies PrivateCompanies
		{
			get { return _aPrivateCompanies; }
			set { _aPrivateCompanies = value; }
		}

		
		private ShareCollection _shares;
		public ShareCollection Shares
		{
			get { return _shares; }
			set { _shares = value; }
		}


		// share handling routines
		public void UpdateShareTables()
		{
			foreach (Share share in _shares)
			{
				if (share.President)
				{
					_nNumShares[share.PublicCompany.Index] += 2;
					_bPresShares[share.PublicCompany.Index] = true;
				}
				else
				{
					_nNumShares[share.PublicCompany.Index]++;
				}
			}
		}

		public void ResetShareIntraRoundInfo()
		{
			for (int i = 0; i < _nNumShares.Length; i++)
				_nNumShares[i] = 0;
			
			for (int i = 0; i < _bPresShares.Length; i++)
				_bPresShares[i] = false;
		}

		public void ResetShareInterRoundInfo()
		{
			for (int i = 0; i < _bSoldShares.Length; i++)
				_bSoldShares[i] = false;
		}

        public int CalculateTotalCompanyShares(int nCompanyIndex, bool bCountHalfShares)
        {
            int             nTotalCompanyShares = 0;
            PublicCompany   company             = Game.PublicCompanies[nCompanyIndex];
            
            // can own up to (60%) in the white and yellow and (100%) in the brown
            // what this means in reality is "6" could be 5 or 6 certificates and "10" is 9 or 10 certificates
            if (company.StockMarketPosition != null)	// if it's open - check, else of course we are
            {
                if (company.Name != "CGR")
                {
                    nTotalCompanyShares = _nNumShares[nCompanyIndex];
                }
                else
                {
                    int nCGRShareTotal = 0;

                    foreach (Share share in Shares)
                    {
                        if (share.PublicCompany.Name == "CGR")
                            nCGRShareTotal += share.Percentage;
                    }

                    // if we have any CGR shares, calculate how many certificates it really is
                    if (nCGRShareTotal > 0)
                    {
                        // calculate full shares as 1 plus any remaining 1/2 share as 1
                        nTotalCompanyShares = (nCGRShareTotal / 10);

                        // if "under", we don't count 1/2 shares, but if "over", we do
                        if (bCountHalfShares)
                            nTotalCompanyShares += (nCGRShareTotal % 2);
                    }
                }
            }

            return nTotalCompanyShares;
        }
        
        public bool IsUnderCompanyShareLimit(int nCompanyIndex)
		{
            int             nTotalCompanyShares = CalculateTotalCompanyShares(nCompanyIndex, false);
            bool            bUnderLimit         = true;
            PublicCompany   company             = Game.PublicCompanies[nCompanyIndex];

            if (company.StockMarketPosition != null)	// if it's open - check, else of course we are
                bUnderLimit = (nTotalCompanyShares < company.StockMarketPosition.CertificateLimit);
            
            return bUnderLimit;
        }
        
        public bool IsOverCompanyShareLimit(int nCompanyIndex)
        {
            int             nTotalCompanyShares = CalculateTotalCompanyShares(nCompanyIndex, true);
            bool            bOverLimit          = false;
            PublicCompany   company             = Game.PublicCompanies[nCompanyIndex];

            if (company.StockMarketPosition != null)	// if it's open - check, else of course we aren't
                bOverLimit = (nTotalCompanyShares > company.StockMarketPosition.CertificateLimit);

            return bOverLimit;
        }

        public int CalculateTotalCertificates(int nCompanyIndex, bool bCountingOverLimit)
        {
            int nCertificates   = 0;
            int nCGRShareTotal  = 0;

            foreach (Share share in Shares)
            {
                if (share.PublicCompany.StockMarketPosition.CountsAgainstCertificateLimit)
                {
                    // check if we have any CGR shares and what their percentage might be
                    if (share.PublicCompany.Name != "CGR")
                        nCertificates++;
                    else
                        nCGRShareTotal += share.Percentage;
                }
            }

            // if we have any CGR shares, calculate how many certificates it really is
            if (nCGRShareTotal > 0)
            {
                if (bCountingOverLimit)
                {
                    // calculate full shares as 1 plus any remaining 1/2 share as 1
                    nCertificates += ((nCGRShareTotal / 10) + (nCGRShareTotal % 2));        // for over limit case, always count half shares
                }
                else
                {
                    bool bBuyingFullShare = Game.PublicCompanies[nCompanyIndex].FullShares;
                    
                    // calculate full shares as 1 plus any remaining 1/2 share as 1
                    if (bBuyingFullShare)
                        nCertificates += ((nCGRShareTotal / 10) + (nCGRShareTotal % 2));    // if buying a full share, count 1/2 share
                    else
                        nCertificates += (nCGRShareTotal / 10);                             // if buying a half share, don't count 1/2 share
                }
            }

            return nCertificates;
        }

        public bool IsUnderCertificateLimit(int nCompanyIndex)
		{
            int nCertificates   = CalculateTotalCertificates(nCompanyIndex, false);
			
			// if we are buying a white certificate count it as 1, otherwise a yellow/brown can be bought regardless
			if ((Game.PublicCompanies[nCompanyIndex].StockMarketPosition == null) ||	// Joe: null means we're in process of buying president's share
				(Game.PublicCompanies[nCompanyIndex].StockMarketPosition.CountsAgainstCertificateLimit))
                return nCertificates < Game.CertificateLimit;
            else
                return true;
		}

        public bool IsOverCertificateLimit()
        {
            int nCertificates = CalculateTotalCertificates(-1, true);

            return nCertificates > Game.CertificateLimit;
        }

        public bool BuyPrivateCompany(Player seller, PrivateCompany company, int nPrice)
		{
			bool	bPurchased	= false;
			
			if (Cash >= nPrice)
			{
				seller.PrivateCompanies.Remove(company);	// remove private from player

				PrivateCompanies.Add(company);				// add to my collection

				Cash		-= nPrice;						// take the cash from me
				seller.Cash += nPrice;						// add the cash to the other player

				bPurchased = true;
			}

			return bPurchased;
		}
		
		public void ClosePublicCompany(int nCompanyIndex)
		{
			bool	bFound	= true;

			while (bFound)
			{
				bFound = false;

				foreach (Share share in Shares)
				{
					if (share.PublicCompany.Index == nCompanyIndex)	// is this in the correct company
					{
						Shares.Remove(share);						// remove it
						bFound = true;
						break;
					}
				}
			}
		}
        
		public void ClosePrivateCompanies()
		{
            for (int i = PrivateCompanies.Count - 1; i >= 0; i--)
            {
                PrivateCompany company = PrivateCompanies[i];

                PrivateCompanies.Remove(company);
            }
		}

		public bool CanSellAnyShares()
		{
			foreach (Share share in Shares)
			{
				if (CanSellShares(share.PublicCompany, share.President ? 2 : 1))
					return true;
			}
			return false;
		}

		public bool CanSellShares(PublicCompany company, int nSharesToSell)
		{
			bool bCanSellShares = false;
			Game game = this.Game;
			Player dumpPlayer = null;
			int nCompanyIndex = company.Index;
			int nMaxOMShares = 5;    // could be 5 or 10 depending on half or full shares
			int nSharesInBank = company.OpenMarketShares.Count;
			bool bCanDumpCompany = false;
			bool bWouldTransfer = false;
			bool bPresident = false;

			if (!game.FirstStockRound)											// is it NOT the 1st stock round
			{
				if (true)   // company.Floated)											// has company floated (sold correct # of shares)
				{
					if (this._nNumShares[nCompanyIndex] > 0)					// must own some shares of it
					{
						if (this._nNumShares[nCompanyIndex] >= nSharesToSell)	// must own at least that many
						{
							// only applies to CGR but we might need to adjust 50% from 5 to 10 shares
							if (!company.FullShares)
								nMaxOMShares = 10;

							if (nSharesToSell <= (nMaxOMShares - nSharesInBank))
							{
								// see if we can dump the company on someone else
								SeeIfWeCanDumpCompanyOnAnotherPlayer(nCompanyIndex, ref dumpPlayer, ref bPresident);

								// does someone owe more that 2 shares besides me?
								bCanDumpCompany = dumpPlayer != null;
								bWouldTransfer = false;

								// if we have a target, check if it would tranfer presidency
								if (bCanDumpCompany)
									bWouldTransfer = ((this._nNumShares[nCompanyIndex] - nSharesToSell) < dumpPlayer._nNumShares[nCompanyIndex]);

								// if we can dump but aren't transfering, then set dump president as none
								if (bCanDumpCompany && !bWouldTransfer)
									dumpPlayer = null;

								// we can sell these shares if...
								// 1. we are not the president
								// 2. we are the president and not forced sale (can't transfer presidency)
								// 3. we are the presidnt, a forced sale but not of the selling RR
								// 4. we are the president but not transfering
								bCanSellShares = ((!bPresident) ||
													(bPresident && bWouldTransfer && bCanDumpCompany && !game.ForcedStockSalePending) ||
													(bPresident && bWouldTransfer && bCanDumpCompany && game.ForcedStockSalePending && game.ForcedStockSaleCompany.Index != nCompanyIndex) ||
													(bPresident && !bWouldTransfer && (this._nNumShares[nCompanyIndex] - nSharesToSell) >= 2));
							}
						}
					}
				}
			}
			return bCanSellShares;
		}

		private void SeeIfWeCanDumpCompanyOnAnotherPlayer(int nCompanyIndex, ref Player dumpPlayer, ref bool bPresident)
		{
            int     nPos = 0;
            int     nCompanyPresidentPos = 0;
            int     nMaxShares = 0;
            int     nCurrentPlayerShares = 0;
            Game    game            = this.Game;
            string  sPlayer         = this.Name;
            Player  player          = null;
            Player  sellingPlayer   = this;	// selling player is always 'this'
            Player  presidentPlayer = null;

            dumpPlayer = null;
            bPresident = false;
            presidentPlayer = game.PublicCompanies[nCompanyIndex].Owner;

            // step 0.  find the player selling
            foreach (Player currentPlayer in game.Players)
            {
                if (currentPlayer == presidentPlayer)
                    nCompanyPresidentPos = nPos;

                //if (currentPlayer.Priority)
                //    sellingPlayer = currentPlayer;	-- selling player is always 'this'

                nPos++;
            }

            // if seller is not president, then we can't dump RR so return
            if (sellingPlayer != presidentPlayer)
                return;

            bPresident = true;
            
            nPos = nCompanyPresidentPos + 1;    // +1 for player to left of president

            // step 1.  next player to president to end
            while (nPos < game.Players.Count)
            {
                player = game.Players[nPos++];
                nCurrentPlayerShares = player._nNumShares[nCompanyIndex];

                // must own at least 2 shares to get dumped on
                if (nCurrentPlayerShares >= 2 && nCurrentPlayerShares > nMaxShares)
                {
                    nMaxShares = nCurrentPlayerShares;
                    dumpPlayer = player;
                }
            }

            nPos = 0;

            // step 2.  beginning to president
            while (nPos < nCompanyPresidentPos)
            {
                player = game.Players[nPos++];
                nCurrentPlayerShares = player._nNumShares[nCompanyIndex];

                // must own at least 2 shares to get dumped on
                if (nCurrentPlayerShares >= 2 && nCurrentPlayerShares > nMaxShares)
                {
                    nMaxShares = nCurrentPlayerShares;
                    dumpPlayer = player;
                }
            }
        }
        
		public void SellShares(Player dumpPlayer, int nCompanyIndex, int nSharesToSell, bool bRecordSale)	// put share into bank pool from player hands
		{
			if (dumpPlayer == null)
			{
				// dump to bank and not selling presidents share
				while (nSharesToSell > 0)
				{
					foreach (Share share in Shares)
					{
						if (share.PublicCompany.Index == nCompanyIndex && !share.President)	// is this in the correct company
						{
							Shares.Remove(share);								// take from player
						
							share.PublicCompany.OpenMarketShares.Add(share);	// give to bank
						
							if (bRecordSale)
							{
								Cash	  += share.PublicCompany.MarketValue;	// adjust players cash
								Game.Bank -= share.PublicCompany.MarketValue;

								_nNumShares[nCompanyIndex]--;
							}

							nSharesToSell--;
							break;
						}
					}
				}
			}
			else
			{
				// dump to player so transfer presidents share to other player, dump his 2 to bank and dump n - 2 into bank also
				
				// first dump president share on dumpPlayer
				foreach (Share share in Shares)
				{
					if (share.PublicCompany.Index == nCompanyIndex && share.President)	// is this in the correct company
					{
						Shares.Remove(share);									// take from player
						
						dumpPlayer.Shares.Add(share);							// give to other player
						dumpPlayer.SellShares(null, nCompanyIndex, 2, false);	// dump other players 2 shares
						share.PublicCompany.Owner = dumpPlayer;					// update new company owner
						
						Cash	  += 2 * share.PublicCompany.MarketValue;		// adjust players cash
						Game.Bank -= 2 * share.PublicCompany.MarketValue;

						_nNumShares[nCompanyIndex] -= 2;
						nSharesToSell -= 2;
						break;
					}
				}
				
				// now dump the remaining n - 2 shares if any to the bank
				SellShares(null, nCompanyIndex, nSharesToSell, true); 
			}
			
			if (bRecordSale)
				_bSoldShares[nCompanyIndex] = true;	// mark this company as SOLD for this SR
		}

        public void StealCompany(Player stealFromPlayer, int nCompanyIndex)	// put share into bank pool from player hands
		{
            if (stealFromPlayer != null)
			{
				// first take president share from stealPlayer
                foreach (Share share in stealFromPlayer.Shares)
				{
					if (share.PublicCompany.Index == nCompanyIndex && share.President)	// is this in the correct company
					{
                        stealFromPlayer.Shares.Remove(share);							// take from current owner
						Shares.Add(share);												// give to me
                        share.PublicCompany.Owner = this;					            // update new company president...me!!!
						break;
					}
				}
				
				// now give 2 normal shares to stealPlayer
				int	nShareCount	= 2;
				
				while (nShareCount > 0)
				{
					foreach (Share share in Shares)
					{
						if (share.PublicCompany.Index == nCompanyIndex && !share.President)	// is this in the correct company
						{
							Shares.Remove(share);											// take from player
                            stealFromPlayer.Shares.Add(share);								// give to other player
							break;
						}
					}

					nShareCount--;
				}
			}
		}


		public void PayDividend(int nCompanyIndex, int nDividend)   // dividend is in 1/10 value already
		{
            int nDividendAmount = 0;
            
            // if this is NOT the CGR, then calc payout as normal
            if (nCompanyIndex != Game.PublicCompanies["CGR"].Index)
            {
                nDividendAmount = nDividend * _nNumShares[nCompanyIndex];
            }
            else    // this is CGR so add up share percentages and multiple by dividend 
            {
                int nCGRShareTotal = 0;
                
                foreach (Share share in Shares)
                {
                    if (share.PublicCompany.Index == nCompanyIndex)
                        nCGRShareTotal += share.Percentage;
                }

                // calculate full shares
                nDividendAmount = (nCGRShareTotal / 10) * nDividend;
                
                // do we have half shares?
                if (nCGRShareTotal % 10 != 0)
                    nDividendAmount += (nDividend + (nDividend % 2)) / 2;
            }

            Cash      += nDividendAmount;
            Game.Bank -= nDividendAmount;
        }
		
        public void GetPlayerWorth(ref int nStockTotal, ref int nCash)
		{
			nStockTotal	= 0;
			
			foreach (PublicCompany company in Game.PublicCompanies)
			{
				if (company.StockMarketPosition != null)
				{
					int	nShareValue	= company.StockMarketPosition.Value;

					//need to handle case of game ending before CGR formed
					//so need to deduct $10 from SV for each loan
					if (Game.GameOver && !Game.CGRFormed)
					{
						if (company.Loans > 0)
						{
							nShareValue -= company.Loans * 10;	// deduct $10 per loan	
					
							if (nShareValue < 0)
								nShareValue = 0;
						}
					}
				
					if (_nNumShares[company.Index] > 0)
						nStockTotal += nShareValue * _nNumShares[company.Index];
				}
			}

			nCash = Cash;
		}
        
        public bool PermaPass { get; set; }

        public bool HasActedDuringStockRound()
        {
            bool bReturn = PurchasedSomething || SoldSomething;
            return bReturn;
        }

        public bool CanPermaPass { get; set; }  // This is set when it's not the player's turn so that PermaPasses do not occur if the user sets the PermaPass checkbox during his turn.
    }
}
